<!doctype html>
<html lang="en" class="dark">

<head>
	<meta charset="utf-8">
	<meta name="viewport" content="width=device-width, initial-scale=1">
	<title>MoQ Demo</title>

	<link rel="stylesheet" href="index.css">
	<link rel="icon" type="image/svg+xml" href="/favicon.svg">
</head>

<body class="dark:bg-black p-4">
	<!-- Show if this browser supports everything we need. -->
	<hang-support mode="watch" show="partial"></hang-support>

	<!--
		There's a simple, web component to set up the provided canvas, including some basic controls.
		Note that the path is optional, and can be set via query parameters in index.ts.

		The relay url is loaded from an environment as it contains a generated JWT authentication token.
		Feel free to hard-code it if you have public access configured, like `url="https://relay.moq.dev/anon"`
		NOTE: `http` performs an insecure certificate check. You must use `https` in production.

		The broadcast path is overwritten by the ?path query parameter in index.ts.

		TODO: There's a bug with Big Buck Bunny causing audio to stutter, so we need to increase the latency to 100ms.

		NOTE: `reload` will detect when the broadcast goes offline/online and automatically reconnect.
		TODO: Cloudflare doesn't support it yet (SUBSCRIBE_NAMESPACE), so make sure you remove it if you're using Cloudflare.
	-->
	<hang-watch-ui>
		<hang-watch url="%VITE_RELAY_URL%" path="bbb" muted latency="100" reload>
			<canvas style="width: 100%; height: auto;"></canvas>
		</hang-watch>
	</hang-watch-ui>

	<h3>Other demos:</h3>
	<ul>
		<li><a href="publish.html">Publish a broadcast.</a></li>
		<li><a href="meet.html">Watch a room of broadcasts.</a></li>
		<li><a href="support.html">Check browser support.</a></li>
	</ul>

	<h3>Tips:</h3>
	<p>
		You can find the source code for this demo in <code>js/hang-demo/src/index.html</code>.
		Yes I know it's confusing when a command automatically opens a browser window.
	</p>
	<p>
		This demo uses
		<code>http</code> so it's extra not secure.
		It works by insecurely fetching the certificate hash and telling WebTransport to trust it.
		If you're going to run this code in production, you'll need a valid certificate (ex. LetsEncrypt) and use
		<code>https</code>.
	</p>
	<hr />
	<p>
		You can instanciate the player via the provided <code class="language-html">&lt;hang-watch&gt;</code> <a
			href="https://developer.mozilla.org/en-US/docs/Web/API/Web_components">Web Component</a>.
		Either modify HTML attributes like <code class="language-html">&lt;hang-watch paused&gt;</code> or use the
		Javascript API.
		The Javascript API is still evolving, so I recommend the Web Component for now.
	</p>
	<p>
		You can provide your own canvas element and use CSS to modify it.
		Unfortunately, you can't use the HTML width/height attributes because of how <a
			href="https://developer.mozilla.org/en-US/docs/Web/API/OffscreenCanvas">OffscreenCanvas</a>
		works.
		For example:
	<pre><code class="language-html">&lt;hang-watch url=&quot;http://localhost:4443/&quot; room=&quot;demo&quot; path=&quot;bbb&quot;&gt;
	&lt;!-- Optionally provide a custom canvas element that we can style as needed --&gt;
	&lt;canvas style=&quot;max-width: 100%; height: auto; border-radius: 4px;&quot;&gt;&lt;/canvas&gt;
&lt;/hang-watch&gt;</code></pre>
	</p>
	<p>
		Don't want video? Don't provide a canvas!
		It won't be downloaded, decoded, or rendered.
		This includes when the video is paused, minimized, not in the DOM, or scrolled out of view.
		wowee the bandwidth savings!
	</p>
	<p>
		Audio may start muted because the browser can require user interaction before autoplaying.
		You can unmute it by removing the <code>muted</code> property or calling <code
			class="language-typescript">watch.audio.muted.set(false)</code> via the Javascript API.
		And of course, nothing is downloaded while it's muted.
	</p>
	<hr />
	<p>
		The Javascript API is far more powerful and you can access properties directly:

	<pre><code class="language-typescript">const watch = document.getElementById("watch");
watch.audio.muted.set(true);
</code></pre>
	</p>

	<p>
		All of the properties are reactive using a hand-rolled signals library: `@kixelated/signals`.
		You could use it... or you can use the provided `react` and `solid` helpers:

	<pre><code class="language-typescript">
import { Watch } from "@kixelated/hang";
import solid from "@kixelated/signals/solid";

function Volume(hang: Watch) {
	// Switch to `react` if you're using React, duh.
	const volume = solid(hang.volume);

	// Return a div that displays the volume.
	return &lt;div&gt;
		Volume: {volume()}
	&lt;/div&gt;
}
		</code></pre>
	</p>
	<p>
		Using something more niche? There's also a <code
			class="language-typescript">subscribe()</code> method to trigger a callback on change.

	<pre><code class="language-typescript">
const cleanup = hang.volume.subscribe((volume) => {
	document.getElementById("volume-value").textContent = `${volume * 100}%`;
});

// Cleanup the subscription when no longer needed.
cleanup();
		</code></pre>
	</p>
	<hr>
	<p>
		The connection and broadcast are automatically reloaded.
		Try running multiple terminals and kill the broadcast to see what happens.

	<pre><code class="language-bash"># Run the relay and web server in another terminal or the background.
just relay &
just web &

just pub bbb
# Kill it with ctrl+C

# Republish the same broadcast, the player will reconnect.
just pub bbb
		</code></pre>
	</p>
	<p>
		If the Big Bunny is making you sick, you can use other inferior test videos or the <a
			href="publish.html">publish demo</a>.
		For example,
		Try running <code class="language-bash">just pub tos</code> in a new terminal and then <a
			href="index.html?path=tos" target="_blank">watch robots bang</a>.
		This command uses ffmpeg to produce a fragmented MP4 file piped over stdout and then sent over the network.
		Yeah it's pretty gross.
	</p>
	<p>
		If you want to do things more efficiently, you can use the GStreamer plugin from the separate
		<a href="https://github.com/kixelated/hang-gst">hang-gst repository</a>.
		It's pretty crude and doesn't handle all pipeline events; contributions welcome!
	</p>
</body>

<script type="module" src="index.ts"></script>

</html>
